﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using ImpEx.GUI;
using ImpEx.Engine;

namespace ImpEx.GUI
{
    internal class ContentTreeViewControl : TreeView
    {
        private static List<string> _fileTypes = new List<string>(new[]
        {
        ".accdb", ".accdc", ".accde", ".asax", ".ascx", ".asmx", ".asp", ".aspx", ".bmp", ".c16", ".cat", ".change", ".chm",
        ".config", ".css", ".db", ".dib", ".disc", ".doc", ".docp", ".docx", ".dot", ".dotx", ".dvd", ".dwp", ".dwt", ".eml",
        ".est", ".fwp", ".gen", ".gif", ".hlp", ".hta", ".htm", ".htmdoc", ".htmfp", ".htmppt", ".htmpub", ".htmxls", ".htt",
        ".inf", ".ini", ".jfif", ".jpe", ".jpeg", ".jpg", ".js", ".jse", ".log", ".m16", ".manage", ".master", ".mht", ".mhtml",
        ".mhtpub", ".mpd", ".mpp", ".mps", ".mpt", ".mpw", ".mpx", ".msg", ".msi", ".msp", ".npie", ".o16", ".ocx", ".odc", ".odcc", ".odcd", ".odct"
        });


        private bool _checkHandleEnabled = true;
        private int _contentTreeUpdateRef;
        private Font _fontBold;
        private Font _fontRegular;

        public ContentTreeViewControl()
        {
            DrawNode += DrawNodeHandle;
            NodeMouseClick += ContentTreeViewControl_NodeMouseClick;
            AfterCheck += AfterCheckHandle;
            BeforeExpand += new TreeViewCancelEventHandler(ContentTreeViewControl_BeforeExpand);

            this.AfterCollapse += new TreeViewEventHandler(ContentTreeViewControl_AfterCollapse);

            _fontRegular = new Font(Font, FontStyle.Regular);
            _fontBold = new Font(Font, FontStyle.Bold);
        }

        void ContentTreeViewControl_AfterCollapse(object sender, TreeViewEventArgs e)
        {
            e.Node.Nodes.Clear();
            if (((WSSObject)e.Node.Tag).HasChildrens())
                e.Node.Nodes.Add("", "");
        }


        void ContentTreeViewControl_BeforeExpand(object sender, TreeViewCancelEventArgs e)
        {
            EnsureNodeChild(e.Node);
            e.Node.Nodes.Clear();
            this.InsChildsSPSItems(e.Node, (WSSObject) e.Node.Tag, true);
        }

        void EnsureNodeChild(TreeNode node)
        {
            if (node.Tag is WSSObject)
            {
                WSSObject o = (WSSObject) node.Tag;
                if (!o.IsChildrenLoaded)
                {
                    Cursor = Cursors.WaitCursor;
                    WSSObject root = (WSSObject) this.Nodes[0].Tag;
                    root.ProgressBar.Reset().NextRange(1);
                    o.ProgressBar.Start(root.ProgressBar).Reset();
                    o.EnsureChildControls();
                    Cursor = Cursors.Default;
                }
            }
        }

        public event EventHandler RulesChanged;

        private void ContentTreeViewControl_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            SelectedNode = e.Node;
        }

        private void DrawNodeHandle(object sender, DrawTreeNodeEventArgs e)
        {
            WSSObject o = (WSSObject) e.Node.Tag;

            if (o != null)
            {
                e.Node.NodeFont =
                    (e.Node.Checked 
                    || o.GetSelectedChildCount() > 0 
                    || o.HasModifiedChilds()
                    || o.CompareState != CompareStateType.Equal)
                        ? _fontBold
                        : _fontRegular;
            }
            e.DrawDefault = true;
        }

        private void AfterCheckHandle(object sender, TreeViewEventArgs e)
        {
            if (_checkHandleEnabled)
            {
                BeginContentTreeUpdate();

                TreeNode node = e.Node;
                UpdateNodeTitle(node);

                WSSObject obj = node.Tag as WSSObject;
                if (!obj.Editable)
                {
                    _checkHandleEnabled = false;
                    node.Checked = obj.Exportable;
                    _checkHandleEnabled = true;
                }
                else
                {
                    if (obj.Exportable != node.Checked)
                    {
                        obj.Exportable = node.Checked;
                        SetNodeCheckedStatus(node, node.Checked);

                        RulesChanged(this, new EventArgs());
                    }
                }

                EndContentTreeUpdate();
            }
        }

        private void BeginContentTreeUpdate()
        {
            if (_contentTreeUpdateRef == 0)
            {
                BeginUpdate();
            }
            _contentTreeUpdateRef++;
        }

        private void EndContentTreeUpdate()
        {
            _contentTreeUpdateRef--;
            if (_contentTreeUpdateRef == 0)
            {
                EndUpdate();
            }
        }


        private static void SetNodeColor(TreeNode node)
        {
            WSSObject obj = node.Tag as WSSObject;

            if (null == obj)
            {
                node.ForeColor = Color.Gray;
            }
            else
            {
                if (obj.Exportable)
                {
                    node.ToolTipText = (obj.Editable) ? "Exportable" : "Exportable with Parent";

                    if (CompareStateType.Equal == obj.CompareState)
                    {
                        node.ForeColor = Color.DeepPink;
                    }
                    else if (CompareStateType.Created == obj.CompareState)
                    {
                        node.ForeColor = Color.DeepPink;
                        node.ToolTipText += ", New";
                    }
                    else if (CompareStateType.Modified == obj.CompareState)
                    {
                        node.ForeColor = Color.DeepPink;
                        node.ToolTipText += ", Changed";
                    }
                }
                else
                {
                    node.ToolTipText = "";
                    if (CompareStateType.Equal == obj.CompareState)
                    {
                        node.ForeColor = (obj.HasModifiedChilds())? Color.DarkRed : Color.Gray;
                    }
                    else if (CompareStateType.Created == obj.CompareState)
                    {
                        node.ForeColor = Color.Green;
                        node.ToolTipText += "New";
                    }
                    else if (CompareStateType.Modified == obj.CompareState)
                    {
                        node.ForeColor = (obj.HasModifiedChilds()) ? Color.DarkRed : Color.Blue;
                        node.ToolTipText += "Changed";
                    }
                }
            }
            if (!obj.Editable)
            {
                node.ForeColor = Color.Silver;
            }
        }

        public void UpdateNodeTitle(TreeNode tmpNode)
        {
            if (tmpNode == null)
            {
                return;
            }

            WSSObject obj = tmpNode.Tag as WSSObject;
            switch (obj.StaticName)
            {
                case "SPSWeb":
                    tmpNode.ImageIndex = 0;
                    tmpNode.Text = obj.Name;
                    break;

                case "SPSList":
                    tmpNode.ImageIndex = 1;
                    tmpNode.Text = obj.Name;
                    break;

                case "SPSFolder":
                case "FSDirectory":
                    tmpNode.ImageIndex = 2;
                    tmpNode.Text = obj.Name;
                    break;

                case "SPSDocumentLibrary":
                    tmpNode.ImageIndex = 3;
                    tmpNode.Text = obj.Name;
                    break;

                case "SPSFile":
                case "FSFile":
                    {
                        string ext = (Path.GetExtension(obj.Name).ToLowerInvariant());
                        int id = _fileTypes.IndexOf(ext);
                        if (id == -1)
                            tmpNode.ImageIndex = 4;
                        else
                            tmpNode.ImageIndex = 5 + id;

                        tmpNode.Text = obj.Name;
                        break;
                    }
                case "SPSListItem":
                    tmpNode.ImageIndex = 4;
                    tmpNode.Text = obj.Name;
                    break;

                default:
                    tmpNode.ImageIndex = -1;
                    tmpNode.Text = string.Format("{0}", obj.Name);
                    break;
            }

            if (!obj.Editable)
            {
                tmpNode.Text = tmpNode.Text.Trim() + " ₪";
            }

            if (obj.CompareState == CompareStateType.Modified)
            {
                tmpNode.Text = tmpNode.Text.Trim() + " (*)";
            }

            if (obj.CompareState == CompareStateType.Created)
            {
                tmpNode.Text = tmpNode.Text.Trim() + " (+)";
            }

            tmpNode.Text = tmpNode.Text + ((obj.ExportChildren) ? "" : " - Dont Include New Childs");
            tmpNode.SelectedImageIndex = tmpNode.ImageIndex;

            string m = tmpNode.Text.Trim();
            int mm = ((m.Length/3) + 1);
            while (mm-- != 0)
            {
                m += " ";
            }
            tmpNode.Text = m;
        }

        public void InsertSPSObject(TreeNode node, WSSObject obj, ContextMenuStrip contextMenu, bool childs)
        {
            if (!obj.ShowInUITree)
            {
                return;
            }

            BeginContentTreeUpdate();

            TreeNode newNode = new TreeNode();
            newNode.Tag = obj;
            newNode.Checked = obj.Exportable;
            newNode.ContextMenuStrip = contextMenu;

            UpdateNodeTitle(newNode);
            SetNodeColor(newNode);
            newNode.NodeFont = _fontBold;

            ((null == node) ? Nodes : node.Nodes).Add(newNode);

            InsChildsSPSItems(newNode, obj, childs);
            EndContentTreeUpdate();
        }

        private void InsChildsSPSItems(TreeNode node, WSSObject obj, bool childs)
        {
            BeginContentTreeUpdate();
            if (obj.IsChildrenLoaded && childs)
            {
                foreach (KeyValuePair<string, WSSCollection> pair in obj.ChildrenCollection)
                {
                    SortAndAddToTree(node, pair, node.ContextMenuStrip);
                }
            }
            else
            {
                if (obj.HasChildrens())
                    node.Nodes.Add("", "");
            }
            EndContentTreeUpdate();
        }

        private string GetExtension(string m)
        {
            int i = m.LastIndexOf('.');
            return (i == -1) ? string.Empty : m.Substring(i);
        }

        private int CompareRule(WSSObject A, WSSObject B)
        {
            int c = A.StaticName.CompareTo(B.StaticName);
            if (c != 0)
                return c;

            try
            {
                c = GetExtension(A.Name).CompareTo(GetExtension(B.Name));
                if (c != 0)
                    return c;
            }
            catch
            {
            }

            return A.Name.CompareTo(B.Name);
        }

        private void SortAndAddToTree(TreeNode node, KeyValuePair<string, WSSCollection> pair,
                                      ContextMenuStrip contextMenu)
        {
            List<WSSObject> rules = new List<WSSObject>();
            foreach (WSSObject obj in pair.Value)
            {
                rules.Add(obj);
            }
            rules.Sort(CompareRule);

            foreach (WSSObject obj in rules)
            {
                InsertSPSObject(node, obj, contextMenu, false);
            }
        }

        private void SetNodeCheckedStatus(TreeNode node, bool Checked)
        {
            if (node.Tag is WSSObject)
            {
                WSSObject obj = (WSSObject) node.Tag;
                obj.Exportable = Checked;

                node.Checked = Checked;
                SetNodeColor(node);

                if (obj.ExportChildren)
                {
                    EnsureNodeChild(node);
                    foreach (TreeNode subNode in node.Nodes)
                    {
                        SetNodeCheckedStatus(subNode, Checked);
                    }
                }
            }
        }
    }
}